package io.gitee.jesse205.activitylauncher.feature.activitylist

import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.ContextMenu
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.AdapterView
import android.widget.EditText
import android.widget.GridView
import android.widget.SearchView
import android.widget.TextView
import androidx.annotation.RequiresApi
import io.gitee.jesse205.activitylauncher.R
import io.gitee.jesse205.activitylauncher.app.BaseActivity
import io.gitee.jesse205.activitylauncher.util.IntentCompat
import io.gitee.jesse205.activitylauncher.util.copyText
import io.gitee.jesse205.activitylauncher.util.isActionBarSupported
import io.gitee.jesse205.activitylauncher.util.isMenuSearchBarSupported
import io.gitee.jesse205.activitylauncher.util.isNavigationGestureSupported
import io.gitee.jesse205.activitylauncher.util.isSupportEdgeToEdge
import io.gitee.jesse205.activitylauncher.util.messageResId
import io.gitee.jesse205.activitylauncher.util.parentsDoNotClipChildrenAndPadding
import io.gitee.jesse205.activitylauncher.util.patch.CollapseActionViewMenuItemPatch
import io.gitee.jesse205.activitylauncher.util.showToast
import io.gitee.jesse205.activitylauncher.util.temporarilyClearFocus

class ActivityListActivity : BaseActivity<ActivityListViewModel>(), AdapterView.OnItemClickListener,
    ActivityListViewModel.ActivityListActivityStateListener {
    override val stateClass = ActivityListViewModel::class.java
    private val adapter: ActivityListAdapter by lazy { ActivityListAdapter(this) }

    private val rootLayout: ViewGroup by lazy { findViewById(R.id.root_layout) }
    private val gridContainer: ViewGroup by lazy { rootLayout.findViewById(R.id.grid_container) }
    private val gridView: GridView by lazy { gridContainer.findViewById(R.id.grid) }
    private val emptyLayout: ViewGroup by lazy { gridContainer.findViewById(android.R.id.empty) }
    private val emptyText: TextView by lazy { emptyLayout.findViewById(R.id.empty_text) }
    private val progressLayout: ViewGroup by lazy { rootLayout.findViewById(R.id.progress_layout) }
    private val progressText: TextView by lazy { progressLayout.findViewById(R.id.progress_text) }

    private var freshMenuItem: MenuItem? = null
    private var searchView: SearchView? = null

    override fun onCreateState(): ActivityListViewModel {
        return ActivityListViewModel(
            packageName = intent.extras?.getString(IntentCompat.EXTRA_PACKAGE_NAME)
        )
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_grid)

        val isActionBarInitialized = if (isActionBarSupported) {
            setupActionBar()
        } else {
            false
        }
        if (!isActionBarInitialized) {
            setupSearchLayout()
        }

        if (state.packageName.isBlank()) {
            showToast(R.string.toast_app_not_installed)
            finish()
            return
        }

        runCatching {
            packageManager.getApplicationInfo(state.packageName, 0)
        }.onFailure {
            showToast(R.string.toast_app_not_installed)
            finish()
            return
        }.onSuccess {
            setTitle(it.loadLabel(packageManager))
        }

        gridView.apply {
            emptyView = emptyLayout
            adapter = this@ActivityListActivity.adapter
            onItemClickListener = this@ActivityListActivity
            registerForContextMenu(this)
            if (isNavigationGestureSupported && theme.isSupportEdgeToEdge) {
                parentsDoNotClipChildrenAndPadding(rootLayout)
            }
        }

        progressText.apply {
            text = getString(R.string.label_getting_activities)
        }

        emptyText.apply {
            text = getString(R.string.label_empty_activities)
        }

        state.bind(this, this)
        onActivitiesUpdate(state.activities)
        onActivitiesLoadingUpdate(state.isActivitiesLoading)
        if (!state.isActivitiesLoadingOrLoaded) {
            loadActivities()
        }
    }

    private fun setupSearchLayout() {
        // HONEYCOMB 以上采用 ActionBar 中的 SearchView
        val searchLayout = findViewById<ViewStub>(R.id.search_layout).inflate()
        val searchInput = searchLayout.findViewById<EditText>(R.id.search_input)
        searchInput.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {

            }

            override fun beforeTextChanged(
                s: CharSequence?,
                start: Int,
                count: Int,
                after: Int
            ) {

            }

            override fun onTextChanged(
                s: CharSequence?,
                start: Int,
                before: Int,
                count: Int
            ) {
                adapter.filter.filter(s)
            }
        })
    }

    @RequiresApi(Build.VERSION_CODES.HONEYCOMB)
    private fun setupSearchBar() {
        searchView?.apply {
            queryHint = getString(R.string.hint_search_activities)
            setOnQueryTextListener(object : SearchView.OnQueryTextListener {
                override fun onQueryTextChange(newText: String?): Boolean {
                    adapter.filter.filter(newText)
                    return true
                }

                override fun onQueryTextSubmit(query: String?): Boolean {
                    return false
                }
            })
        }
    }

    @RequiresApi(Build.VERSION_CODES.HONEYCOMB)
    private fun setupActionBar(): Boolean {
        val actionBar = getActionBar()?.apply {
            setDisplayHomeAsUpEnabled(true)
            if (!isMenuSearchBarSupported) {
                setDisplayShowTitleEnabled(resources.configuration.screenWidthDp >= 600)
                setDisplayShowCustomEnabled(true)
                searchView = SearchView(this@ActivityListActivity).apply {
                    isIconifiedByDefault = false
                }
                customView = searchView
                setupSearchBar()
            }
        }
        return actionBar != null
    }

    override fun onCreateContextMenu(menu: ContextMenu, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
        super.onCreateContextMenu(menu, v, menuInfo)
        when (menuInfo) {
            is ActivityListAdapter.ContextMenuInfo -> onCreateActivityListContextMenu(menu, menuInfo)
        }
    }

    fun onCreateActivityListContextMenu(menu: ContextMenu, menuInfo: ActivityListAdapter.ContextMenuInfo) {
        menu.setHeaderTitle(menuInfo.activity.getOrLoadLabel(packageManager))
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            menu.setGroupDividerEnabled(true)
        }
        menuInflater.inflate(R.menu.menu_activity_list_item, menu)

    }


    override fun onContextItemSelected(item: MenuItem): Boolean {
        when (val menuInfo = item.menuInfo) {
            is ActivityListAdapter.ContextMenuInfo -> onActivityListContextItemSelected(item, menuInfo)
            else -> return super.onContextItemSelected(item)
        }
        return true
    }

    fun onActivityListContextItemSelected(item: MenuItem, menuInfo: ActivityListAdapter.ContextMenuInfo) {
        when (item.itemId) {
            R.id.menu_copy_display_name -> copyDisplayName(menuInfo.activity)
            R.id.menu_copy_class_name -> copyClassName(menuInfo.activity)
            R.id.menu_copy_intent_uri -> copyIntentUri(menuInfo.activity.activityInfo)

        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        getMenuInflater().inflate(R.menu.menu_activity_list, menu)
        freshMenuItem = menu.findItem(R.id.menu_refresh)
        freshMenuItem!!.isEnabled = !state.isActivitiesLoading
        if (isMenuSearchBarSupported) {
            menu.findItem(R.id.menu_search).apply {
                isVisible = true
                // 修复启用返回按钮时候工具栏多出边距
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    actionBar?.let {
                        setOnActionExpandListener(CollapseActionViewMenuItemPatch(it))
                    }
                }
            }.also {
                searchView = it.actionView as SearchView
                setupSearchBar()
            }
        }
        return true
    }

    @Suppress("DEPRECATION")
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> onBackPressed()
            R.id.menu_refresh -> loadActivities()
            else -> return super.onOptionsItemSelected(item)
        }
        return true
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            // 当窗口获取焦点后，会自动弹出软键盘，临时清除焦点以避免弹出输入法
            searchView?.temporarilyClearFocus()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        adapter.destroy()
    }

    override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        val activityInfo: AppActivityItem = adapter.getItem(position)
        launchActivity(activityInfo.activityInfo)
    }

    private fun copyDisplayName(item: AppActivityItem) {
        copyText(getString(R.string.label_display_name), item.getOrLoadLabel(packageManager))
    }

    private fun copyClassName(item: AppActivityItem) {
        copyText(getString(R.string.label_class_name), item.name)
    }

    private fun copyIntentUri(activityInfo: ActivityInfo) {
        val intent = Intent(Intent.ACTION_MAIN)
        intent.setClassName(activityInfo.packageName, activityInfo.name)
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        copyText(getString(R.string.label_intent_uri), intent.toUri(Intent.URI_INTENT_SCHEME))
    }

    private fun loadActivities() {
        state.loadActivities(application)
    }

    private fun launchActivity(activityInfo: ActivityInfo) {
        val intent = Intent(Intent.ACTION_MAIN)
        intent.setClassName(activityInfo.packageName, activityInfo.name)
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        runCatching {
            startActivity(intent)
        }.onFailure {
            Log.w(TAG, "launchActivity failed: ", it)
            showToast(it.messageResId ?: R.string.error_unknown)
        }
    }

    override fun onActivitiesUpdate(activities: List<AppActivityItem>?) {
        adapter.setActivities(activities ?: listOf())
    }

    override fun onActivitiesLoadingUpdate(isActivitiesLoading: Boolean) {
        val visibleWhenLoading = if (isActivitiesLoading) View.VISIBLE else View.GONE
        val visibleWhenNotLoading = if (isActivitiesLoading) View.GONE else View.VISIBLE
        progressLayout.visibility = visibleWhenLoading
        gridContainer.visibility = visibleWhenNotLoading
        freshMenuItem?.isEnabled = !isActivitiesLoading
    }

    override fun onPackageNameUpdate(packageName: String) {}

    companion object {
        private const val TAG = "ActivityListActivity"
        fun launch(context: Context, packageName: String) {
            val intent = Intent(context, ActivityListActivity::class.java)
            intent.putExtra(IntentCompat.EXTRA_PACKAGE_NAME, packageName)
            context.startActivity(intent)
        }
    }
}
